home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 May: Tool Chest / Developer CD Series May 1996 (Tool Chest) (Apple Computer) (1996).iso / Sample Code / SCSI Samples 1.0 / SCSI Simple Sample 06⁄15 ƒ / Src / AsyncSCSI.c next >
Encoding:
C/C++ Source or Header  |  1994-06-16  |  18.4 KB  |  474 lines  |  [TEXT/KAHL]

  1. /*                                    AsyncSCSI.c                                    */
  2. /*
  3.  * AsyncSCSI.c
  4.  * Copyright © 1992-93 Apple Computer Inc. All Rights Reserved.
  5.  *
  6.  * Talk to the Macintosh SCSI Manager 4.3 using the "new" interface. This is a
  7.  * synchronous call that executes a single SCSI Command on a device. It will
  8.  * automatically calls Request Sense on errors. Note: this is intended as a self-
  9.  * contained illustration of the Asynchronous SCSI Manager and is (intentionally)
  10.  * inefficient in that it does many "bureaucratic" things that would normally
  11.  * be done once when an application or device driver is initialized.
  12.  *
  13.  * Calling Sequence:
  14.  *        OSErr                AsyncSCSI(
  15.  *                DeviceIdent                scsiDevice,
  16.  *                const SCSI_CommandPtr    scsiCommand,
  17.  *                unsigned short            cmdBlockLength,
  18.  *                Boolean                    writeToDevice,
  19.  *                Ptr                        bufferPtr,
  20.  *                unsigned long            transferSize,
  21.  *                unsigned short            scsiHandshake[handshakeDataLength],
  22.  *                SCSI_Sense_Data            *senseDataPtr,
  23.  *                unsigned long            senseDataSize,
  24.  *                unsigned long            completionTimeout,
  25.  *                unsigned short            *stsBytePtr,
  26.  *                unsigned long            *actualTransferCount
  27.  *            );
  28.  * The parameters have the following meaning:
  29.  *
  30.  *    scsiDevice            The SCSI host bus, target, lun that we are talking to.
  31.  *    scsiCommand            The SCSI Command Block (6, 10, or 12 bytes).
  32.  *    cmdBlockLength        The length in bytes of the command block.
  33.  *    writeToDevice        TRUE if this command writes to the device. FALSE if this
  34.  *                        command reads from the device or does not require a data
  35.  *                        phase.
  36.  *    bufferPtr            The user data buffer for Read/Write commands. It should be
  37.  *                        NULL if a data transfer phase is not used for this command.
  38.  *                        (e.g. for Test Unit Ready).
  39.  *    transferSize        The total number of bytes to transfer.
  40.  *    scsiHandshake        This will be copied to the scsiHandshake field in the
  41.  *                        SCSIAction parameter block. Useful handshake fields
  42.  *                        include the following:
  43.  *                            0                No handshake: do one blind transfer,
  44.  *                            512,0            Normal disk blind transfer,
  45.  *                            1,511,0            Disk blind transfer if the device
  46.  *                                            can stall before *and* after the
  47.  *                                            first byte in a sector.
  48.  *                        If scsiHandshake is NULL, a polled transfer will be done..
  49.  *    senseDataPtr        If not NULL and the original request failed, this will
  50.  *                        be filled with the result from a Request Sense operation.
  51.  *    senseDataSize        This is the size of the Request Sense data buffer.
  52.  *    completionTimeout    The timeout (in Ticks) for the command. This should be
  53.  *                        short for disks, but must be long for tape devices and
  54.  *                        some setup requests, such as Mode Select.
  55.  *    stsBytePtr            This short is set to the byte returned in the device's
  56.  *                        Status Phase.
  57.  *    actualTransferCount    This will be set to the number "cycles" through the TIB
  58.  *                        loop. This should equal the number of bytes transferred if
  59.  *                        transferCount is set to one. (Ignored if NULL.)
  60.  * Return codes:
  61.  *    noErr            normal
  62.  *    unimpErr        SCSI Manager 4.3 is not available: the calling function should
  63.  *                    call the "old" (Inside Mac IV) SCSI Manager.
  64.  *    statusErr        Device returned "Check condition" and SCSI Manager was able
  65.  *                    to successfully issue Request Sense. There is data in the
  66.  *                    Sense Record, but you cannot assume that the original request
  67.  *                    succeeded.
  68.  *    paramErr        Could not determine the command length.
  69.  *    scsi...            Other error
  70.  */
  71. #include <GestaltEqu.h>
  72. #include <Memory.h>
  73. #include <Events.h>
  74. #include <Errors.h>
  75. #include "MacSCSICommand.h"
  76. #ifndef TRUE
  77. #define TRUE        1
  78. #define FALSE        0
  79. #endif
  80.  
  81. /*
  82.  * These globals are defined by SCSISimpleSample.h, and are needed only for
  83.  * testing and debugging.
  84.  */
  85. extern Boolean                    gEnableSelectWithATN;
  86. extern Boolean                    gDoDisconnect;
  87. extern Boolean                    gDontDisconnect;
  88.  
  89. static void                     NextFunction(void);        /* For HoldMemory size    */
  90. /*
  91.  * This should be specified at application/driver startup, and not the
  92.  * inefficient "call Gestalt each time" shown here.
  93.  */
  94. static Boolean                    IsVirtualMemoryRunning(void);
  95. #ifndef CLEAR
  96. /*
  97.  * Cheap 'n dirty memory clear routine.
  98.  */
  99. #define CLEAR(record) do {                                \
  100.         register char    *ptr = (char *) &record;        \
  101.         register long    size;                            \
  102.         for (size = sizeof record; size > 0; --size)    \
  103.             *ptr++ = 0;                                    \
  104.     } while (0)
  105.  
  106. #endif
  107.  
  108. /*
  109.  * These are bitmasks for the vmHoldMask variable. A bit is set if its associated
  110.  * memory element has been held in protected (non-paged) memory.
  111.  */
  112. #define kHoldFunction            0x0001                /* AsyncSCSI function code    */
  113. #define kHoldStack                0x0002                /* Local variables            */
  114. #define kHoldUserBuffer            0x0004                /* User data buffer, if any    */
  115. #define kHoldSenseBuffer        0x0008                /* Sense buffer, if any        */
  116. #define kHoldParamBlock            0x0010                /* SCSIExecIOPB                */
  117.  
  118. OSErr                        AsyncSCSI(
  119.         DeviceIdent                scsiDevice,            /* -> Bus/target/LUN        */
  120.         const SCSI_CommandPtr    scsiCommand,        /* The actual scsi command    */
  121.         unsigned short            cmdBlockLength,        /* -> Length of CDB            */
  122.         Boolean                    writeToDevice,        /* TRUE to write            */
  123.         Ptr                        bufferPtr,            /* -> user data buffer        */
  124.         unsigned long            transferSize,        /* How much to transfer        */
  125.         unsigned short            scsiHandshake[handshakeDataLength],
  126.         SCSI_Sense_Data            *senseDataPtr,        /* Request Sense results    */
  127.         unsigned long            senseDataSize,        /* Request Sense data size    */
  128.         unsigned long            completionTimeout,    /* Ticks to wait            */
  129.         unsigned short            *stsBytePtr,        /* <- status phase byte        */
  130.         unsigned long            *actualTransferCount
  131.     );
  132.  
  133. /*
  134.  * Execute a SCSI command.
  135.  * Returns the final status as noted above.
  136.  */
  137. OSErr
  138. AsyncSCSI(
  139.         DeviceIdent                scsiDevice,            /* -> Bus/target/LUN        */
  140.         const SCSI_CommandPtr    scsiCommand,        /* The actual scsi command    */
  141.         unsigned short            cmdBlockLength,        /* -> Length of CDB            */
  142.         Boolean                    writeToDevice,        /* TRUE to write            */
  143.         Ptr                        bufferPtr,            /* -> user data buffer        */
  144.         unsigned long            transferSize,        /* How much to transfer        */
  145.         unsigned short            scsiHandshake[handshakeDataLength],
  146.         SCSI_Sense_Data            *senseDataPtr,        /* Request Sense results    */
  147.         unsigned long            senseDataSize,        /* Request Sense data size    */
  148.         unsigned long            completionTimeout,    /* Ticks to wait            */
  149.         unsigned short            *stsBytePtr,        /* <- status phase byte        */
  150.         unsigned long            *actualTransferCount
  151.     )
  152. {
  153.         OSErr                    status;                /* Result code                */
  154.         SCSIBusInquiryPB        busInquiryPB;        /* Used for SCSIBusInquiry    */
  155.         register SCSIExecIOPB    *execIOPBPtr;        /* Used for SCSIAction        */
  156. #define PB                        (*execIOPBPtr)        /* PB references paramBlock    */
  157.         unsigned long            execIOPBSize;        /* SCSIAction pb size        */
  158.         Boolean                    enableSelectWithATN; /* Ok to select with ATN?    */
  159.         register short            i;                    /* Move command block index    */
  160.         
  161.         /*
  162.          * These two flags are used to record whether the asynchronous SCSI
  163.          * Manager is present on this machine (one flag records whether it is
  164.          * present, the other whether we've tested for presence). This is
  165.          * reasonable for applications. However, this is not sufficient for
  166.          * drivers or other code that may be called before the system has been
  167.          * completely initialized, as the asynchronous SCSI Manager may be
  168.          * installed by a System Extension.
  169.          */
  170.         static Boolean            gHasAsyncSCSIManager;
  171.         static Boolean            gTestedForAsyncSCSIManager;
  172.         /*
  173.          * The following parameters are used to manage virtual memory.
  174.          */
  175.         unsigned short            vmHoldMask;
  176.         unsigned long            vmFunctionSize;
  177.         void                    *vmProtectedStackBase;    /* Last local var    */
  178. /*
  179.  * These values are used to compute the size of the stack that we must hold in
  180.  * protected (non-virtual) memory. kSCSIManagerStackEstimate is an estimate.
  181.  */
  182. #define kSCSILocalVariableSize    ( \
  183.         (unsigned long) (((Ptr) &status) - ((Ptr) &vmProtectedStackBase))    \
  184.     )
  185. #define kSCSIManagerStackEstimate 512
  186. #define kSCSIProtectedStackSize (kSCSIManagerStackEstimate + kSCSILocalVariableSize)
  187.  
  188.         status = noErr;
  189.         vmHoldMask = 0;
  190.         /*
  191.          * If the asynchronous SCSI Manager exists, we will allocate a parameter
  192.          * block that will be freed when the function exits (if allocation
  193.          * succeeded). In a real application or driver, this would be allocated
  194.          * once, as part of the per-device initialization.
  195.          */
  196.         execIOPBPtr = NULL;
  197.         /*
  198.          * First, make sure that the asynchronous SCSI Manager has been installed.
  199.          * In an application, this may be done once when the application starts.
  200.          * In a driver, this test must be deferred until the Process Manager
  201.          * is running.
  202.          */
  203.         if (gTestedForAsyncSCSIManager == FALSE) {
  204.             gTestedForAsyncSCSIManager = TRUE;
  205.             gHasAsyncSCSIManager = (
  206.                     NGetTrapAddress(_SCSIAtomic, OSTrap)
  207.                     != NGetTrapAddress(_Unimplemented, OSTrap)
  208.                 );
  209.         }
  210.         if (gHasAsyncSCSIManager == FALSE) {
  211.             status = unimpErr;
  212.             goto exit;
  213.         }
  214.         /*
  215.          * Allocate a parameter block for this bus. In a production application
  216.          * or driver, this would be done, once, along with other initialization.
  217.          * Note that we always clear the parameter block: the SCSI Manager will
  218.          * fail with an error if some fields it expects to be NULL (such as
  219.          * the queue link) are non-NULL.
  220.          */
  221.         CLEAR(busInquiryPB);
  222.         busInquiryPB.scsiPBLength = sizeof busInquiryPB;
  223.         busInquiryPB.scsiFunctionCode = SCSIBusInquiry;
  224.         busInquiryPB.scsiDevice = scsiDevice;
  225.         SCSIAction((SCSI_PB *) &busInquiryPB);
  226.         status = busInquiryPB.scsiResult;
  227.         if (status != noErr)
  228.             goto exit;
  229.         /*
  230.          * If we are running on a Quadra 840-AV with a CD300, the Macintosh will
  231.          * hang if it tries to access LUN 1. We check for this problem in two
  232.          * ways: by examining a global "LUN 1 test ok" flag, and by checking
  233.          * whether the bug was fixed, either by running on later hardware or by
  234.          * installing a System Update.
  235.          */
  236.         enableSelectWithATN =
  237.                 gEnableSelectWithATN
  238.                 && (busInquiryPB.scsiWeirdStuff & scsiTargetDrivenSDTRSafe) != 0; 
  239.         /*
  240.          * Allocate a parameter block for this request using the size that
  241.          * was returned in the busInquiry parameter block.
  242.          */
  243.         execIOPBSize = busInquiryPB.scsiIOpbSize;
  244.         execIOPBPtr = (SCSIExecIOPB *) NewPtrClear(execIOPBSize);
  245.         if (execIOPBPtr == NULL) {
  246.             status = MemError();
  247.             goto exit;
  248.         }
  249.         /*
  250.          * Setup the parameter block for the user's request.
  251.          */
  252.         PB.scsiPBLength = execIOPBSize;
  253.         PB.scsiFunctionCode = SCSIExecIO;
  254.         PB.scsiTimeout = completionTimeout;
  255.         PB.scsiDevice = scsiDevice;
  256.         PB.scsiCDBLength = cmdBlockLength;
  257.         /*
  258.          * Copy the command block into the SCSI ExecIO Parameter block to
  259.          * centralize everything for debugging. Also, this is one less thing to
  260.          * have to lock into physical memory. Note that BlockMove of six, ten,
  261.          * or twelve bytes is very inefficient. Utility application software
  262.          * should move the bytes using an inline operation, and drivers will
  263.          * either store a pointer to a per-request command data block or
  264.          * explicitly construct the command in the paramater block.
  265.          */
  266.         for (i = 0; i < cmdBlockLength; i++)
  267.             PB.scsiCDB.cdbBytes[i] = scsiCommand->scsi[i];
  268.         /*
  269.          * Specify the transfer direction, if any, and setup the other SCSI
  270.          * operation flags. scsiSIMQNoFreeze prevents the SCSI Manager from
  271.          * blocking further operation if an error is detected.
  272.          */
  273.         PB.scsiFlags = scsiSIMQNoFreeze;
  274.         if (bufferPtr == NULL || transferSize == 0)
  275.             PB.scsiFlags |= scsiDirectionNone;
  276.         else {
  277.             /*
  278.              * If the user did not specify a scsi handshake field, select "polled"
  279.              * transfers, otherwise, select "blind."
  280.              */
  281.             PB.scsiTransferType = (scsiHandshake == NULL)
  282.                         ? scsiTransferPolled
  283.                         : scsiTransferBlind;
  284.             PB.scsiDataPtr = (unsigned char *) bufferPtr;
  285.             PB.scsiDataLength = transferSize;
  286.             PB.scsiDataType = scsiDataBuffer;
  287.             PB.scsiFlags |= (writeToDevice) ? scsiDirectionOut : scsiDirectionIn;
  288.             if (scsiHandshake != NULL) {
  289.                 for (i = 0; i < handshakeDataLength; i++)
  290.                     PB.scsiHandshake[i] = scsiHandshake[i];
  291.             }
  292.         }
  293.         /*
  294.          * Do we support autosense?
  295.          */
  296.         if (senseDataPtr != NULL && senseDataSize >= 5) {
  297.             senseDataPtr->errorCode = 0;
  298.             PB.scsiSensePtr = (unsigned char *) senseDataPtr;
  299.             PB.scsiSenseLength = senseDataSize;
  300.         }
  301.         else {
  302.             PB.scsiFlags |= scsiDisableAutosense;
  303.         }
  304.         /*
  305.          * Look at the global flags that can be set to configure the asynchronous
  306.          * SCSI Manager - these are for testing, and would typically be set
  307.          * permanently to a particular (device-specific) state by a real application.
  308.          */
  309.         if (enableSelectWithATN == FALSE)    /* Enabled by user and SCSI manager? */
  310.             PB.scsiIOFlags |= scsiDisableSelectWAtn;
  311.         if (gDoDisconnect)
  312.             PB.scsiFlags |= scsiDoDisconnect;
  313.         if (gDontDisconnect)
  314.             PB.scsiFlags |= scsiDontDisconnect;
  315.         /*
  316.          * We are now ready to perform the operation. If virtual memory is active
  317.          * however, we must lock down all memory segments that can be potentially
  318.          * "touched" while the SCSI request is being executed. All of this is
  319.          * needed for applications. For drivers, this can generally be ignored as
  320.          * the driver code and driver-specific resources are stored in the System
  321.          * Heap, which is always "held" in physical memory and the Device Manager
  322.          * locks down data buffers when PBRead/PBWrite are called. However, note
  323.          * that if Control or Status calls require data transfers, the driver
  324.          * must explicitly lock the buffer.
  325.          */
  326.         if (IsVirtualMemoryRunning()) {
  327.             /*
  328.              * Virtual memory is active. Lock all of the memory segments that we
  329.              * need in "real" memory (i.e. non-paged pool) for the duration of the
  330.              * call. Since  we need to back out of VM holds if there are errors,
  331.              * we'll use bits in vmHoldMask to record the status of our attempts.
  332.              *
  333.              * Note: in a real application or driver, the user buffers should be
  334.              * held outside of the SCSI Manager code:
  335.              *        HoldMemory(data buffer);
  336.              *        HoldMemory(autosense buffer);
  337.              *        status = CallSCSIManager(...);
  338.              *        UnholdMemory(...);
  339.              *
  340.              * First, hold the MacSCSI function. It starts at AsyncSCSI and
  341.              * extends to the start of the next function. This is marked by a
  342.              * dummy function. The left-margin comments indicate the value
  343.              * of vmHoldCount if the indicated HoldMemory succeeded. This is not
  344.              * needed for drivers.
  345.              */
  346.             vmFunctionSize =
  347.                 (unsigned long) NextFunction - (unsigned long) AsyncSCSI;
  348.             status = HoldMemory(AsyncSCSI, vmFunctionSize);
  349.             if (status == noErr)
  350.                 vmHoldMask |= kHoldFunction;
  351.             if (status == noErr) {
  352.                 /*
  353.                  * Hold a chunk of stack space to call the SCSI Manager and to
  354.                  * protect our local variables. This is always needed, as drivers
  355.                  * can be called from application contexts.
  356.                  */
  357.                 vmProtectedStackBase =
  358.                     (char *) &vmProtectedStackBase - kSCSIManagerStackEstimate;
  359.                 status = HoldMemory(vmProtectedStackBase, kSCSIProtectedStackSize);
  360.                 if (status == noErr)
  361.                     vmHoldMask |= kHoldStack;
  362.             }
  363.             if (status == noErr) {
  364.                 /*
  365.                  * Lock down the parameter block. In this sample, we allocated
  366.                  * the parameter block in the application heap. A driver would
  367.                  * typically allocate it in the System Heap and, hence, would
  368.                  * not require this call.
  369.                  */
  370.                 status = HoldMemory((Ptr) execIOPBPtr, execIOPBSize);
  371.                 if (status == noErr)
  372.                     vmHoldMask |= kHoldParamBlock;
  373.             }        
  374.             if (status == noErr && bufferPtr != NULL) {
  375.                 /*
  376.                  * Lock down the user buffer, if any. In a real-world application
  377.                  * or driver, this would be done before calling the SCSI interface.
  378.                  */
  379.                 status = HoldMemory(bufferPtr, transferSize);
  380.                 if (status != noErr)
  381.                     vmHoldMask |= kHoldStack;
  382.             }
  383.             if (status == noErr && PB.scsiSensePtr != NULL) {
  384.                 /*
  385.                  * Lock down the sense data. A driver would probably allocate one
  386.                  * of these (in a per-transaction buffer) in the System Heap. An
  387.                  * application should lock this before calling the SCSI Manager.
  388.                  */
  389.                 status = HoldMemory(PB.scsiSensePtr, PB.scsiSenseLength);
  390.                 if (status == noErr)
  391.                     vmHoldMask |= kHoldSenseBuffer;
  392.             }
  393.         }
  394.         /*
  395.          * Finally, call the asynchronous SCSI Manager. SCSIAction is synchronous
  396.          * because we did not specify a completion routine.
  397.          */
  398.         if (status == noErr) {
  399.             status = SCSIAction((SCSI_PB *) &PB);
  400.             if (status == noErr)
  401.                 status = PB.scsiResult;
  402.         }
  403.         /*
  404.          * If we held memory, unhold it now.  We ignore UnholdMemory errors:
  405.          * there isn't much we can do about them. Note that this must be
  406.          * done by driver or asynchronous completion routines.
  407.          */
  408. exit:    if ((vmHoldMask & kHoldSenseBuffer) != 0)
  409.             (void) UnholdMemory(PB.scsiSensePtr, PB.scsiSenseLength);
  410.         if ((vmHoldMask & kHoldUserBuffer) != 0)
  411.             (void) UnholdMemory(bufferPtr, transferSize);
  412.         if ((vmHoldMask & kHoldParamBlock) != 0)
  413.             (void) UnholdMemory((Ptr) execIOPBPtr, execIOPBSize);
  414.         if ((vmHoldMask & kHoldStack) != 0)
  415.             (void) UnholdMemory(vmProtectedStackBase, kSCSIProtectedStackSize);
  416.         if ((vmHoldMask & kHoldFunction) != 0)
  417.             (void) UnholdMemory(AsyncSCSI, vmFunctionSize);
  418.         /*
  419.          * Now, look at the result of the operation.
  420.          */
  421.         if (execIOPBPtr != NULL) {
  422.             /*
  423.              * Recover some data to return to the user. 
  424.              */
  425.             if (stsBytePtr != NULL)
  426.                 *stsBytePtr = PB.scsiSCSIstatus;
  427.             if (actualTransferCount != NULL)
  428.                 *actualTransferCount = transferSize - PB.scsiDataResidual;
  429.             /*
  430.              * Note: scsiDataRunError is issued if our transfer request was larger
  431.              * or smaller than the actual transfer length. We need to examine the
  432.              * actual transfer sizes to see how to handle this error. This is
  433.              * not necessarily complete or correct. The intent here is to supress
  434.              * the transfer length error when executing Request Sense or other
  435.              * administrative commands with variable-length result blocks.
  436.              * Note that the user can then recover the actual block length by
  437.              * examining the actualTransferCount parameter.
  438.              */
  439.             if (status == scsiDataRunError                /* Over/underrun error    */
  440.              && writeToDevice == FALSE                    /* But we're reading    */
  441.              && actualTransferCount != NULL                /* And user wants count    */
  442.              && (*actualTransferCount) <= transferSize    /* And its a short read    */
  443.              && (*actualTransferCount) > 0)                /* And some data read?    */
  444.                 status = noErr;                            /* If so, ignore error    */
  445.             /*
  446.              * If the device issued Check Condition and the SCSI Manager was able
  447.              * to retrieve a Request Sense datum, change the error to our private
  448.              * "Check Condition" status.
  449.              */
  450.             if (status == scsiNonZeroStatus
  451.              && (PB.scsiResultFlags & scsiAutosenseValid) != 0)
  452.                  status = statusErr;
  453.              DisposePtr((Ptr) execIOPBPtr);
  454.         }
  455.         return (status);
  456. #undef PB
  457. }
  458.  
  459. static void NextFunction(void) { }    /* Dummy function for AsyncSCSI size    */
  460.  
  461. static Boolean
  462. IsVirtualMemoryRunning(void)
  463. {
  464.         OSErr                        status;
  465.         long                        response;
  466.         
  467.         status = Gestalt(gestaltVMAttr, &response);
  468.         /*
  469.          * VM is active iff Gestalt succeeded and the response is appropriate.
  470.          */
  471.         return (status == noErr && ((response & (1 << gestaltVMPresent)) != 0));
  472. }
  473.  
  474.